/*
 * ProductBLL.java
 *
 * $Id: ProductBLL.java,v 1.6 2009-10-07 10:20:22 mario Exp $
 */
package org.ceteca.explica.server.warehouse.bl;

import java.util.ArrayList;
import java.util.Iterator;

import org.ceteca.explica.core.ExplicaException;
import org.ceteca.explica.core.common.ImageOT;
import org.ceteca.explica.core.util.OperationResult;
import org.ceteca.explica.core.util.ResultMessage;
import org.ceteca.explica.core.util.ResultMessageCatalog;
import org.ceteca.explica.core.warehouse.InterfaceWarehouseErrors;
import org.ceteca.explica.core.warehouse.MeasureUnitOT;
import org.ceteca.explica.core.warehouse.ProductOT;
import org.ceteca.explica.server.ServerContext;
import org.ceteca.explica.server.util.db.CriteriaBean;
import org.ceteca.explica.server.warehouse.dao.MeasureUnitDAO;
import org.ceteca.explica.server.warehouse.dao.ProductDAO;
import org.ceteca.explica.server.warehouse.dao.ProductGroupDAO;

import org.ceteca.explica.core.warehouse.ProductGroupOT;
import org.ceteca.explica.server.warehouse.bl.ProductBLL;

/**
 * Business Logic class that implements the Product
 * business logic processes. 
 * <br/>
 * Responsibilities:
 * <ul>
 * 	<li>Validation of the products.</li>
 * </ul>
 * <br/>
 * @author Mario García García <mario@imagos.es>
 * Copyright (c) 2007 Fundación Centro Tecnolóxico da Carne
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <br/>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <br/>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class ProductBLL {
	/** Unique instance of the ProductBLL class. Singleton pattern */
	private static ProductBLL instance;

	/** Returns the unique instance of the TraceabilityTreeBLL class.
	 * Implements the singleton dessign pattern.
	 * @return TraceabilityTreeBLL, unique instance of TraceabilityTreeBLL.
	 */
	public static final ProductBLL getInstance() {
		if (instance == null)
			instance = new ProductBLL();
		return instance;
	}

	/**
	 * Default constructor.
	 */
	private ProductBLL() {

	}

	/**
	 * Check if the product information typed in by the user is correct.
	 * For a new product user must type in at least:
	 * <ul>
	 * <li>REFERENCE: Numeric</li>
	 * <li>BARCODE: Numeric (valid barcode)</li>
	 * <li>BARCODE_TYPE: Numeric</li>
	 * <li>GROUP: Numeric</li>
	 * <li>DESCRIPTION: String</li>
	 * <li>PRICE: Numeric</li>
	 * <li>VAT_TYPE: Numeric</li>
	 * <li>IMAGE: Byte Array (Optional)</li>
	 * <li>ALLOW_NO_STOCK: Boolean (Optional)</li>
	 * <li>AVERAGE_COST: Numeric (Calculated)</li>
	 * </ul>
	 * @param prod ProductOT, data of the product to validate.
	 * @param msgCatalog ResultMessageCatalog, instance of the catalog of result
	 * messages available for the warehouse module.
	 * @return ArrayList<ResultMessage>, List of messages with the result of the
	 * validation process. It may be an OK message if the validation
	 * was correct, or a list of detailed error messages ready to be showed
	 * to the user in case the validation was incorrect.
	 */
	private ArrayList<ResultMessage> validateProduct(ProductOT prod, ResultMessageCatalog msgCatalog) {
		ArrayList<ResultMessage> result = new ArrayList<ResultMessage>();
/*		
		// Validate product's reference
		if (!Validator.getInstance().isValidNumeric(prod.getReference())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getReference());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_REFERENCE, 
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_REFERENCE_ITEMNAME));
		}
		// Validate product's barcode
		if (!Validator.getInstance().isValidBarcode(prod.getBarcode())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getBarcode());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_BARCODE,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_BARCODE_ENTRY_ITEMNAME));
		}
		// Validate product's barcode type
		// TO_DO: Get list of valid barcode types
		ArrayList lBarcodeTypes = null;
		if (prod.getBarcodeType() == null || !Validator.getInstance().isValidValueInCollection(prod.getBarcodeType(), lBarcodeTypes.toArray())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getBarcode());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_BARCODE_TYPE,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_BARCODE_ENTRY_ITEMNAME));
		}
		ArrayList lGroups = null;
		// Validate product's group
		if (prod.getGroup() == null || !Validator.getInstance().isValidValueInCollection(prod.getGroup().getIdGroup(), lGroups.toArray())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getGroup());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_GROUP,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_DESCRIPTION_ITEMNAME));
		}
		// Validate product's description
		if (Validator.getInstance().isEmpty(prod.getDescription())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getDescription());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_DESCRIPTION,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_DESCRIPTION_ITEMNAME));
		}
		// Validate product's price
		if (prod.getPrice() == null || prod.getPrice().doubleValue() > 0) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getPrice());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_PRICE,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_DESCRIPTION_ITEMNAME));
		}
		ArrayList lVatTypes = null;
		// Validate product's VAT tax type
		if (prod.getVatType() == null || !Validator.getInstance().isValidValueInCollection(prod.getVatType().getIdVat(), lVatTypes.toArray())) {
			ArrayList lParams = new ArrayList();
			lParams.add(prod.getVatType());
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_VAT_TYPE,
					lParams, InterfaceTraceabilityTree.EDIT_PRODUCT_DESCRIPTION_ITEMNAME));
		}
*/
		// If no error has taken place then the validation of the product is OK
		if (result == null || result.size() <= 0) {
			result.add(msgCatalog.newResultMessage(InterfaceWarehouseErrors.ERR_WAREHOUSE_VALIDATION_PRODUCT_OK));
		}
		
		return result;
	}
	
	public ArrayList queryProducts(ArrayList <CriteriaBean> lCriteria) throws ExplicaException {
		// Call the DAO to query the database and get the products.
		ProductDAO dao = new ProductDAO();
		ArrayList lProducts = dao.getProducts(lCriteria);
		
		// Load the products' images from the image files stored in the server resources directory.
		for (int i=0; lProducts != null && i<lProducts.size(); i++) {
			ProductOT prod = (ProductOT)lProducts.get(i);
			System.out.println(String.format("Product: %s --> %d Images: ------------\n%s\n--------------------",
					prod.getReference(), prod.getImages().size(), prod.getImages()));
			if (prod != null)
				this.loadProductImages(prod);
		} // for each product
		
		ServerContext.getInstance().getLogger().info("List of products: " + lProducts);
		
		return lProducts;
	}
	
	public OperationResult delProduct(Long id) throws ExplicaException {
		// Call the DAO to remove the product from the database.
		ProductDAO dao = new ProductDAO();
		return dao.delProduct(id);
	}
	
	public OperationResult delProducts(ArrayList<Long> lSelProducts) throws ExplicaException {
		// Call the DAO to remove the product from the database.
		ProductDAO dao = new ProductDAO();
		return dao.delProducts(lSelProducts);
	}
	
	public OperationResult saveProduct(ProductOT product) throws ExplicaException {
		// Call the DAO to update the database with the product's data
		ProductDAO dao = new ProductDAO();
		return dao.saveProduct(product);
	}
	
	public ProductOT getProduct(Long id) throws ExplicaException {
		// Call the DAO to remove the product from the database.
		ProductDAO dao = new ProductDAO();
		ProductOT prod = dao.getProduct(id.longValue());
		if (prod != null)
			this.loadProductImages(prod);
		
		return prod;
	}
		
	private void loadProductImages(ProductOT prod) {
		if (prod.getImages() != null && prod.getImages().size() > 0) {
			Iterator it = prod.getImages().iterator();
			while (it != null && it.hasNext()) {
				ImageOT imageOT = (ImageOT)it.next();
				imageOT.setContents(ServerContext.getInstance().loadImage(imageOT.getPath()));
			} // while there are more images
		} // if there are any image
	}
	
	public ArrayList queryProductGroups(ArrayList <CriteriaBean> lCriteria) throws ExplicaException {
		// Call the DAO to query the database and get the product groups.
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.getProductGroups(lCriteria);
	}
	public OperationResult delProductGroup(Long id) throws ExplicaException {
		// Call the DAO to remove the product group from the database.
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.delProductGroup(id);
	}
	
	public OperationResult delProductGroups(ArrayList<Long> lSelGroups) throws ExplicaException {
		// Call the DAO to remove the product group from the database.
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.delProductGroups(lSelGroups);
	}
	
	public OperationResult saveProductGroup(ProductGroupOT group) throws ExplicaException {
		// Call the DAO to query the database and get the product groups.
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.saveProductGroup(group);
	}
	
	public ProductGroupOT getProductGroup(Long id) throws ExplicaException {
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.getProductGroup(id.longValue());
	}
	
	public long getNextProductGroupId() throws ExplicaException {
		ProductGroupDAO dao = new ProductGroupDAO();
		return dao.getNextProductGroupId();
	}
	
	public void resetNextProductGroupId(long idGroup) throws ExplicaException {
		ProductGroupDAO dao = new ProductGroupDAO();
		dao.resetNextProductGroupId(idGroup);
	}
	
	public ArrayList queryMeasureUnits(ArrayList <CriteriaBean> lCriteria) throws ExplicaException {
		System.out.println("(((((((((((((()))))))))))))))) " + lCriteria);
		// Call the DAO to query the database and get the measure units.
		MeasureUnitDAO dao = new MeasureUnitDAO();
		return dao.getMeasureUnits(lCriteria);
	}
	public OperationResult delMeasureUnit(Long id) throws ExplicaException {
		// Call the DAO to remove the measure unit from the database.
		MeasureUnitDAO dao = new MeasureUnitDAO();
		return dao.delMeasureUnit(id);
	}
	
	public OperationResult delMeasureUnits(ArrayList<Long> lSelMeasureUnits) throws ExplicaException {
		// Call the DAO to remove the measure unit from the database.
		MeasureUnitDAO dao = new MeasureUnitDAO();
		return dao.delMeasureUnits(lSelMeasureUnits);
	}
	
	public OperationResult saveMeasureUnit(MeasureUnitOT measureUnit) throws ExplicaException {
		// Call the DAO to query the database and get the products.
		MeasureUnitDAO dao = new MeasureUnitDAO();
		return dao.saveMeasureUnit(measureUnit);
	}
	
	public MeasureUnitOT getMeasureUnit(Long id) throws ExplicaException {
		MeasureUnitDAO dao = new MeasureUnitDAO();
		return dao.getMeasureUnit(id.longValue());
	}
	
	/**
	 * Returns tag Id assigned to CVS source file.
	 */
	public static String getRevision() {
		return "$Id: ProductBLL.java,v 1.6 2009-10-07 10:20:22 mario Exp $";
	}
}